package payment

import (
	"context"
	"fmt"
	"github.com/go-pay/gopay"
	"github.com/go-pay/gopay/wechat/v3"
	"myai/core/types"
	"net/http"
	"time"
)

type WechatPayService struct {
	config *types.WechatPayConfig
	client *wechat.ClientV3
}

func NewWechatService(appConfig *types.AppConfig) (*WechatPayService, error) {
	config := appConfig.WechatPayConfig
	if !config.Enabled {
		log.Info("禁用微信支付服务")
		return nil, nil
	}

	priKey, err := readKey(config.PrivateKey)
	if err != nil {
		return nil, fmt.Errorf("读取微信配置的私钥失败：%v", err)
	}

	client, err := wechat.NewClientV3(config.MchID, config.SerialNo, config.ApiV3Key, priKey)
	if err != nil {
		return nil, fmt.Errorf("初始化微信客户端服务时出错：%v", err)
	}

	err = client.AutoVerifySign()
	if err != nil {
		return nil, fmt.Errorf("开启请求完自动验签功能失败：%v", err)
	}
	return &WechatPayService{config: &config, client: client}, nil
}

type WechatPayParams struct {
	OutTradeNo string `json:"out_trade_no"`
	TotalFee   int    `json:"total_fee"`
	Subject    string `json:"subject"`
	ClientIP   string `json:"client_ip"`
	ReturnURL  string `json:"return_url"`
	NotifyURL  string `json:"notify_url"`
}

func (s *WechatPayService) PayUrlNative(params WechatPayParams) (string, error) {
	expire := time.Now().Add(10 * time.Minute).Format(time.RFC3339)
	bm := make(gopay.BodyMap)
	bm.Set("appid", s.config.AppId).
		Set("mchid", s.config.MchID).
		Set("description", params.Subject).
		Set("out_trade_no", params.OutTradeNo).
		Set("time_expire", expire).
		Set("notify_url", params.NotifyURL).
		SetBodyMap("amount", func(mb gopay.BodyMap) {
			mb.Set("total", params.TotalFee).
				Set("currency", "CNY")
		})
	// 下单
	wxRsp, err := s.client.V3TransactionNative(context.Background(), bm)
	if err != nil {
		return "", fmt.Errorf("微信 Native 下单失败: %v", err)
	}

	if wxRsp.Code != wechat.Success {
		return "", fmt.Errorf("微信 Native 生成支付url时的错误状态: %v", wxRsp.Error)
	}
	// 返回生成的二维码
	return wxRsp.Response.CodeUrl, nil
}

func (s *WechatPayService) PayUrlH5(params WechatPayParams) (string, error) {
	expire := time.Now().Add(10 * time.Minute).Format(time.RFC3339)
	bm := make(gopay.BodyMap)
	bm.Set("appid", s.config.AppId).
		Set("mchid", s.config.MchID).
		Set("description", params.Subject).
		Set("out_trade_no", params.OutTradeNo).
		Set("time_expire", expire).
		Set("notify_url", params.NotifyURL).
		SetBodyMap("amount", func(mb gopay.BodyMap) {
			mb.Set("total", params.TotalFee).
				Set("currency", "CNY")
		}).
		SetBodyMap("scene_info", func(mb gopay.BodyMap) {
			mb.Set("payer_client_ip", params.ClientIP).
				SetBodyMap("h5_info", func(b gopay.BodyMap) {
					b.Set("type", "Wap")
				})
		})
	// H5下单
	wxRsp, err := s.client.V3TransactionH5(context.Background(), bm)
	if err != nil {
		return "", fmt.Errorf("微信 H5 下单失败: %v", err)
	}

	if wxRsp.Code != wechat.Success {
		return "", fmt.Errorf("微信 H5 生成支付url时的错误状态: %v", wxRsp.Error)
	}
	// 返回生成的二维码
	return wxRsp.Response.H5Url, nil
}

type NotifyResponse struct {
	Code    string `json:"code"`
	Message string `xml:"message"`
}

// TradeVerify 交易验证
func (s *WechatPayService) TradeVerify(request *http.Request) NotifyVo {
	notifyReq, err := wechat.V3ParseNotify(request)
	if err != nil {
		return NotifyVo{Status: 1, Message: fmt.Sprintf("微信解析通知失败：%v", err)}
	}

	// TODO: 这里验签程序有 Bug，一直报错：crypto/rsa: verification error，先暂时取消验签
	//err = notifyReq.VerifySignByPK(s.client.WxPublicKey())
	//if err != nil {
	//	return fmt.Errorf("error with client v3 verify sign: %v", err)
	//}

	// 解密支付密文，验证订单信息
	result, err := notifyReq.DecryptPayCipherText(s.config.ApiV3Key)
	if err != nil {
		return NotifyVo{Status: Failure, Message: fmt.Sprintf("微信解密 普通支付 回调中的加密信息失败: %v", err)}
	}
	return NotifyVo{
		Status:     Success,
		OutTradeNo: result.OutTradeNo,
		TradeId:    result.TransactionId,
		Amount:     fmt.Sprintf("%.2f", float64(result.Amount.Total)/100),
	}
}
